use crate::frag_shader::FragShader;
use crate::matrix::{Mat4f, Vec2f, Vec3f, Vec3i};
use crate::triangle::Triangle;
use crate::AttrInds;
use crate::Fragment;
use std::collections::HashMap;
// use std::rc::Rc;
use crate::texture::Texture;
pub struct Rasterizer {
    projection: Mat4f, // perspective
    model: Mat4f,      // world pos
    view: Mat4f,       // camera pos
    frame_buffer: Vec<Vec3f>,
    depth_buffer: Vec<f32>,
    width: usize,
    height: usize,
    next_id: usize,
    indices: HashMap<usize, Vec<Vec3i>>,
    positions: HashMap<usize, Vec<Vec3f>>,
    colors: HashMap<usize, Vec<Vec3f>>,
    texcoor: Option<HashMap<usize, Vec<Vec2f>>>,
    normals: Option<HashMap<usize, Vec<Vec3f>>>,
    frag_shader: FragShader,
    // texture_img: Box<Option<image::ImageBuffer<image::Rgb<u8>, Vec<u8>>>>,
    pub texture_img: Option<Texture>,
    pub inds: Vec<AttrInds>, // texture_img: &image::ImageBuffer<image::Rgb<u8>, Vec<u8>>,
                             // text_map:
}
impl Rasterizer {
    pub fn run(&mut self) {
        let mut rotate = 140.0_f32;
        let frame_buf: Vec<u32> = self.frame_buffer.iter().map(|v| v.to_rgb()).collect();
        let mut screen = minifb::Window::new(
            "Title",
            self.width,
            self.height,
            minifb::WindowOptions::default(),
        )
        .expect("Unable to open Window");
        screen.limit_update_rate(Some(std::time::Duration::from_millis(10)));
        self.sample();
        while screen.is_open() && !screen.is_key_released(minifb::Key::Escape) {
            screen
                .update_with_buffer(&frame_buf, self.width, self.height)
                .expect("Update screen failed!");
            // for key in screen.get_keys_pressed(minifb::KeyRepeat::No) {
            //     match key {
            //         Key::A => {
            //             rotate = rotate + 1.0;
            //             self.model =
            //                 Mat4f::rotate(Vec3f::unit_y(), rotate.to_radians()) * Mat4f::scale(2.5);
            //             self.sample();
            //         }
            //         Key::D => {
            //             self.model =
            //                 Mat4f::rotate(Vec3f::unit_y(), rotate.to_radians()) * Mat4f::scale(2.5);
            //             self.sample();
            //         }
            //         _ => (),
            //     }
            // }
        }
    }
    pub fn sample(&mut self) {
        println!("inds len = {}", self.inds.len());
        self.inds.clone().iter().for_each(|i| self.draw(i.clone()));
    }
    pub fn find_index(&self, x: usize, y: usize) -> usize {
        x + (self.height - y - 1) * self.width
    }
    pub fn set_pixel(&mut self, point: Vec2f, color: Vec3f) {
        // println!("set pixel {:?}", point);
        let index = self.find_index(point[0] as usize, point[1] as usize);
        self.frame_buffer[index] = color;
    }
    pub fn draw(&mut self, inds: AttrInds) {
        match inds.frag_type {
            Fragment::Triangle => {
                let pos = self.positions[&inds.pos_id].clone();
                // println!(
                // "max z = {}",
                // pos.iter()
                // .fold(0.0, |ac, c| if c[2] > ac { c[2] } else { ac })
                // );
                let ind = self.indices[&inds.ind_id].clone();
                let col = self.colors[&inds.col_id].clone();
                let mut tex = vec![];
                if let Some(texcoor) = &self.texcoor {
                    tex = texcoor[&inds.tex_id].clone();
                }
                let mut norms = vec![];
                if let Some(norms_t) = &self.normals {
                    norms = norms_t[&inds.nor_id].clone();
                }
                let mvp = self.projection * self.view * self.model;
                for i in ind {
                    // every triangle
                    let homo_pos =
                        i.0.map(|idx| pos[idx as usize])
                            .map(move |p| mvp * p.to_homo_point())
                            .map(move |p| p / p[3])
                            .map(move |p| p.truncate());
                    // print!("this z = {:?}", homo_pos.map(|p| p[2]));
                    let homo_pos = homo_pos.map(|p| {
                        // 挪到正数的下标上
                        [
                            (p[0] / 2.0 + 0.5) * self.width as f32,
                            (p[1] / 2.0 + 0.5) * self.height as f32,
                            (-p[2] / 2.0 + 0.5), // p[2], // NOTE: z * (100-0.1)/2. + (100+0.1)/2. ?
                        ]
                        .into()
                    });
                    let mut tri = Triangle::new(homo_pos, i.0.map(|idx| col[idx as usize]));
                    if tex.len() > 0 {
                        tri.payload.tex_coords = Some(i.0.map(|idx| tex[idx as usize]));
                    }
                    if norms.len() > 0 {
                        tri.payload.normals = Some(i.0.map(|idx| norms[idx as usize]));
                    }
                    self.rasterize_triangle(tri);
                }
            } // _ => unimplemented!("Only support Triangle Fragment"),
        }
    }
    pub fn bounding_box_of(&self, t: &Triangle) -> [f32; 4] {
        let xs = t.positions.map(|elem| elem[0]);
        let ys = t.positions.map(|elem| elem[1]);
        // println!("xs:{:?}\nys:{:?}", xs, ys);
        let xmin = xs.iter().fold(xs[0], |ac, &cur| ac.min(cur));
        let xmax = xs.iter().fold(xs[0], |ac, &cur| ac.max(cur));
        let ymin = ys.iter().fold(ys[0], |ac, &cur| ac.min(cur));
        let ymax = ys.iter().fold(ys[0], |ac, &cur| ac.max(cur));
        [xmin, xmax, ymin, ymax]
    }
    pub fn rasterize_triangle(&mut self, t: Triangle) {
        // println!("rasterize triangle");
        let [xmin, xmax, ymin, ymax] = self.bounding_box_of(&t).map(|elem| elem.trunc() as usize);
        // println!("bounding box={:?}\n", [xmin, xmax, ymin, ymax]);
        let [z0, z1, z2] = t.positions.map(|p| p[2]);
        for x in xmin..xmax {
            for y in ymin..ymax {
                let p = Vec2f::from([x as f32, y as f32]);
                if t.contains(p) {
                    let [b0, b1, b2] = t.barycenter_for(p);
                    println!("b1,b2,b3={:?}", [b0, b1, b2]);
                    let depth = b0 * z0 + b1 * z1 + b2 * z2;
                    let idx = self.find_index(x, y);
                    if self.depth_buffer[idx] < depth {
                        // let color = (self.frag_shader)(&self, &t, [b0, b1, b2]);
                        let color = Vec3f::from([255., 255., 255.]);
                        self.depth_buffer[idx] = depth;
                        self.set_pixel([x as f32, y as f32].into(), color);
                    }
                }
            }
        }
    }
    pub fn rasterize_wireframe(&mut self, t: Triangle) {
        let p = t.positions;
        // println!("raster positions:{:?}", p);
        self.draw_line(p[0], p[1], t.payload.colors.unwrap()[0]);
        self.draw_line(p[1], p[2], t.payload.colors.unwrap()[1]);
        self.draw_line(p[2], p[0], t.payload.colors.unwrap()[2]);
    }
    // Bresenham's
    pub fn draw_line(&mut self, begin: Vec3f, end: Vec3f, color: Vec3f) {
        // println!("drawline {:?}->{:?}", begin, end);
        let mut steep: bool = false;
        let mut x0 = begin[0] as usize;
        let mut y0 = begin[1] as usize;
        let mut x1 = end[0] as usize;
        let mut y1 = end[1] as usize;
        if x0.abs_diff(x1) < y0.abs_diff(y1) {
            // if the line is steep, we transpose the image
            std::mem::swap(&mut x0, &mut y0);
            std::mem::swap(&mut x1, &mut y1);
            steep = true;
        }
        if x0 > x1 {
            std::mem::swap(&mut x0, &mut x1);
            std::mem::swap(&mut y0, &mut y1);
        }
        // println!("range {x0}..{x1}");
        for x in x0..x1 {
            // println!("for x={}", x);
            let t = (x - x0) as f32 / (x1 - x0) as f32;
            let y = y0 as f32 * (1. - t) + y1 as f32 * t;
            if steep {
                self.set_pixel([y, x as f32].into(), color); // if transposed. de-transpose
            } else {
                self.set_pixel([x as f32, y].into(), color);
            }
        }
    }
    // begin with 1
    pub fn get_next_id(&mut self) -> usize {
        self.next_id += 1;
        self.next_id
    }
    pub fn aspect_ratio(&self) -> f32 {
        self.width as f32 / self.height as f32
    }
    pub fn new(width: usize, height: usize, frag_shader: FragShader) -> Self {
        Self {
            projection: Mat4f::identity(),
            model: Mat4f::identity(),
            view: Mat4f::identity(),
            depth_buffer: vec![std::f32::INFINITY; width * height],
            width,
            height,
            frame_buffer: vec![Vec3f::zero(); width * height],
            next_id: 0,
            indices: HashMap::new(),
            positions: HashMap::new(),
            colors: HashMap::new(),
            texcoor: None,
            normals: None,
            frag_shader,
            texture_img: None,
            inds: vec![],
        }
    }
    pub fn load_positions(&mut self, positions: Vec<Vec3f>) -> usize {
        // self.positions.extend_one((self.get_next_id(), positions));
        let id = self.get_next_id();
        self.positions.insert(id, positions);
        id
    }
    pub fn load_indices(&mut self, indices: Vec<Vec3i>) -> usize {
        let id = self.get_next_id();
        self.indices.insert(id, indices);
        id
    }
    pub fn load_texcoor(&mut self, texcoor: Vec<Vec2f>) -> usize {
        let id = self.get_next_id();
        let refs = self.texcoor.get_or_insert(HashMap::new());
        refs.insert(id, texcoor);
        id
    }
    pub fn load_norm(&mut self, normals: Vec<Vec3f>) -> usize {
        let id = self.get_next_id();
        let refs = self.normals.get_or_insert(HashMap::new());
        refs.insert(id, normals);
        id
    }
    pub fn load_colors(&mut self, colors: Vec<Vec3f>) -> usize {
        let id = self.get_next_id();
        self.colors.insert(id, colors);
        id
    }
    pub fn set_model(&mut self, m: Mat4f) {
        self.model = m;
    }
    pub fn set_view(&mut self, v: Mat4f) {
        self.view = v;
    }
    pub fn set_project(&mut self, p: Mat4f) {
        self.projection = p;
    }
}
